package storage;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;

/**
 * Developed for NTT - All is an Entity
 * Questa classe è la diretta responsabile del caricamento di configurazione di tutto il sistema
 * @author Alessandro Pollace
 * @version 0.1
 */
public class ConfigLoader {

    /***** DATA *****/
    // SYSTEM
    /**
     * Numero massimo di entity memorizzate nella cache
     */
    static private int maxNumberEntityInCache = 0;
    /**
     * Lingua di default del sistema
     */
    static private String language = null;
    /**
     * root dei file di configurazione
     */
    static private String root = "/home/alessandro/Documenti/NTT Alpha 1/";
    /**
     * ID temporale della cache, tanto più il suo valore è vicino allo zero, tanto più è vecchio il suo caricamento nella cache
     */
    static private int entityLoadedIDTemp[] = null;
    /**
     * Nome delle entity caricate
     */
    static private String entityLoaded[] = null;
    /**
     * Nome della chiave primaria dell'entity
     */
    static private String primaryKeyName[] = null;
    /**
     * Nomi degli attributi
     */
    static private String attributeName[][] = null;
    /**
     * Caption degli attributi, caricata nella lingua di default del sistema
     */
    static private String attributeCaption[][] = null;
    /**
     * Descrizione degli attributi, caricata nella lingua di default del sistema
     */
    static private String attributeDescription[][] = null;
    /**
     * Tipo dell'attributo
     */
    static private String attributeType[][] = null;
    /**
     * Permessi di lettura per ogni attributo
     */
    static private String attributeRead[][] = null;
    /**
     * Permessi di modifica per ogni attributo
     */
    static private String attributeModify[][] = null;
    /**
     * Permessi di inserimento per ogni attributo
     */
    static private String inserible[][] = null;
    /**
     * Indicatore di unicità per ogni attributo
     */
    static private String unique[][] = null;
    /**
     * Indica i permessi di creazione per ogni entity
     */
    static private String creation[] = null;
    /**
     * Indica se la cancellazione è di tipo logico o meno
     */
    static private String logicalDelete[] = null;
    /**
     * Proprietà della lingua caricate dal file
     */
    static private Properties langCaption = null;

    /************************************************************************/
    /**
     * Costruttore della classe ConfigLoader
     *
     * @throws Exception impossibile caricare configurazione
     */
    public ConfigLoader() throws Exception {
        this.loadSysConfiguration();
    }

    /************************************************************************/
    /***** METHOD - PRIVATE *****/
    /**
     * Questo metodo carica la configurazione del sistema.
     */
    private synchronized void loadSysConfiguration() throws Exception {
        // DATA
        FileInputStream sysConfFile;
        Properties sysConfig = new Properties();
        // OPERATION
        if (language == null) {
            try {
                // Apro il file di configurazione
                sysConfFile = new FileInputStream(root + "conf/sys.conf");
                sysConfig.load(sysConfFile);
                // leggo la configurazione dal file
                language = sysConfig.getProperty("Language");
                maxNumberEntityInCache = Integer.parseInt(sysConfig.getProperty("maxNumberEntityInCache"));
            } catch (IOException e1) {
                // Non è stato possibile leggere il file, uso la configurazione
                // standard
                language = "it";
                maxNumberEntityInCache = 10;
                throw new Exception(
                        "Error-EntityConfigLoader-Constructor-NotLoadConfig:UseDefaultConfiguration");
            } finally {
                // in tutti i casi assegno una dimensione alle variabili.
                entityLoaded = new String[maxNumberEntityInCache];
                entityLoadedIDTemp = new int[maxNumberEntityInCache];
                primaryKeyName = new String[maxNumberEntityInCache];
                attributeName = new String[maxNumberEntityInCache][];
                attributeCaption = new String[maxNumberEntityInCache][];
                attributeDescription = new String[maxNumberEntityInCache][];
                attributeType = new String[maxNumberEntityInCache][];
                attributeRead = new String[maxNumberEntityInCache][];
                attributeModify = new String[maxNumberEntityInCache][];
                inserible = new String[maxNumberEntityInCache][];
                unique = new String[maxNumberEntityInCache][];
                logicalDelete = new String[maxNumberEntityInCache];
                creation = new String[maxNumberEntityInCache];
            }

            try {
                sysConfFile.close();
            } catch (IOException e) {
                throw new Exception(
                        "Error-EntityConfigLoader-Constructor-NotCloseFile");
            }
        }
    }

    /**
     * Carica dal file la configurazione dell'entity indicata dal parametro
     * entityName
     *
     * @param entityName
     *            Nome dell'entità di cui si intende caricare la configurazione.
     * @throws Exception Impossibile trovare configurazione entity
     */
    private synchronized String[] loadAttributeProperties(String entityName,
            String propertiesName) throws Exception {
        // DATA
        ConfigLoader configL = new ConfigLoader();
        Properties conf = new Properties();
        InputStream is = null;
        String tmp[];
        try {
            is = new FileInputStream(configL.getRoot() + "conf/entity/" + entityName + ".conf");
            conf.load(is);
        } catch (Exception e) {
            throw new Exception(
                    "Error-EntityConfigLoader-loadAttributeProperties-EntityNotFound");
        }

        tmp = conf.getProperty(propertiesName).split("##");

        try {
            is.close();
        } catch (IOException e) {
            throw new Exception(
                    "Error-Entity-ConfigLoader-loadAttributeProperties-NotCloseFile");
        }
        return tmp;
    }

    /**
     * Carica tutte le properties di una Entity da file e le inserisce nella
     * cache
     *
     * @param entityName
     *            Nome dell'entità di cui si interessa sapere le proprietà
     */
    private synchronized void loadEntityProperties(String entityName)
            throws Exception {
        int moreOld;
        moreOld = this.moreOldEntity();
        entityLoaded[moreOld] = entityName;

        attributeName[moreOld] = this.loadAttributeProperties(entityName, "ATTRIBUTE");

        attributeCaption[moreOld] = this.loadAttributeProperties(entityName, "CAPTION." + language);

        attributeDescription[moreOld] = this.loadAttributeProperties(entityName, "DESCRIPTION_ATTRIBUTE." + language);

        attributeType[moreOld] = this.loadAttributeProperties(entityName, "TYPE_ATTRIBUTE");

        attributeRead[moreOld] = this.loadAttributeProperties(entityName, "READ_ATTRIBUTE");

        attributeModify[moreOld] = this.loadAttributeProperties(entityName, "MODIFY_ATTRIBUTE");

        inserible[moreOld] = this.loadAttributeProperties(entityName, "INSERIBLE");

        unique[moreOld] = this.loadAttributeProperties(entityName, "UNIQUE");

        primaryKeyName[moreOld] = this.loadAttributeProperties(entityName, "PRIMARYKEY")[0];

        logicalDelete[moreOld] = this.loadAttributeProperties(entityName, "LOGICAL_DELETE")[0];

        creation[moreOld] = this.loadAttributeProperties(entityName, "CREATION")[0];
    }

    /**
     * Aggiorna il tempo di latenza, ossia assegna alle entity caricate un
     * numero che va da 0 fino al massimo di possibili entity contenute in
     * memoria che vale il massimo se l'entity è stata appena caricata e vale
     * zero se sono avvenuti maxNumberEntityInCache caricamenti senza che sia
     * stata chiamata.
     *
     * @param index
     *            indice dell'entity appena chiamata
     */
    private synchronized void refreshTimeNotUse(int index) {
        for (int i = 0; i < maxNumberEntityInCache; i++) {
            if (entityLoadedIDTemp[i] > 0) {
                entityLoadedIDTemp[i]--;
            }
        }
        entityLoadedIDTemp[index] = maxNumberEntityInCache;
    }

    /**
     * Restituisce l'indice della configurazione più vecchia o una di quelle a
     * parimerito.
     *
     * @return La configurazione delle'Entity che non viene toccata da più
     *         tempo.
     */
    private int moreOldEntity() {
        int old = 0;
        for (int i = 0; i < maxNumberEntityInCache; i++) {
            if (entityLoadedIDTemp[old] > entityLoadedIDTemp[i]) {
                old = i;
            }
        }
        return old;
    }

    /**
     * Questo metodo prende in input un array di string di tipo YES o NO e restituisce un valore boolean
     * @param s Stringa
     * @return TRUE = YES , FALSE = NO
     */
    private boolean[] convertStringYesToBoolean(String s[]) {
        boolean b[] = new boolean[s.length];
        for (int i = 0; i < s.length; i++) {
            if (s[i].equals("YES")) {
                b[i] = true;
            } else {
                b[i] = false;
            }
        }
        return b;

    }

    /**
     * Questo metodo prende in input una string di tipo YES o NO e restituisce un valore boolean
     * @param s Stringa
     * @return TRUE = YES , FALSE = NO
     */
    private boolean convertStringYesToBoolean(String s) {
        boolean b;
        if (s.equals("YES")) {
            b = true;
        } else {
            b = false;
        }
        return b;

    }

/**
 * Ritorna l'indice in cui è possibile trovare la configurazione dell'entity chiamata per nome -1 se nn la trova
 * @param entityName nome dell'entity
 * @return indice
 * @throws Exception Impossibile caricare l'entity
 */
    private int getEntityIndex(String entityName) throws Exception {
        for (int i = 0; i < maxNumberEntityInCache; i++) {
            if (entityName.equals(entityLoaded[i]) == true) {
                return i;
            }
        }
        this.loadEntityProperties(entityName);
        return this.getEntityIndex(entityName);
    }

    /************************************************************************/
    /***** METHOD - PROTECTED *****/
    /**
     * cerca nella cache a disposizione l'entity indicata da entityName, nel
     * caso in cui non sia presente scarica la configurazione più vecchia e
     * carica quella dell'entity appena richiesta.
     *
     * @param entityName
     *            nome dell'entità di ci intendono caricare i nomi degli
     *            attributi.
     * @return nome dell'attributo
     * @throws Exception
     */
    protected synchronized String[] getAttributeName(String entityName)
            throws Exception {
        int index;
        index = getEntityIndex(entityName);
        this.refreshTimeNotUse(index);
        return attributeName[index];
    }

    /**
     * Questo metodo permette di sapere i nomi degli attributi presi in esame
     *
     * @param entityName
     *            Nome dell'entità di cui si interessa sapere le proprietà
     * @return Array di stringhe
     * @throws Exception
     */
    protected synchronized String getPrimaryKeyName(String entityName)
            throws Exception {
        int index;
        index = getEntityIndex(entityName);
        this.refreshTimeNotUse(index);
        return primaryKeyName[index];
    }

    /**
     * Questo metodo permette di sapere quali sono i tipi degli attributi
     * dell'entity presa in esame
     *
     * @param entityName
     *            Nome dell'entità di cui si interessa sapere le proprietà
     * @return Array di string
     * @throws Exception Impossibile trovare entity
     */
    protected synchronized String[] getAttributeType(String entityName)
            throws Exception {
        int index;
        index = getEntityIndex(entityName);
        this.refreshTimeNotUse(index);
        return attributeType[index];
    }

    /**
     * Questo metodo permette di sapere quali sono i permessi in lettura degli attributi
     * dell'entity presa in esame
     *
     * @param entityName
     *            Nome dell'entità di cui si interessa sapere le proprietà
     * @return Array di string
     * @throws Exception Impossibile trovare entity
     */
    protected synchronized String[] getReadPermits(String entityName)
            throws Exception {
        int index;
        index = getEntityIndex(entityName);
        this.refreshTimeNotUse(index);
        return attributeRead[index];
    }

    /**
     * Questo metodo permette di sapere quali sono i permessi in modifica degli attributi
     * dell'entity presa in esame
     *
     * @param entityName
     *            Nome dell'entità di cui si interessa sapere le proprietà
     * @return Array di string
     * @throws Exception Impossibile trovare entity
     */
    protected synchronized String[] getModifyPermits(String entityName)
            throws Exception {
        int index;
        index = getEntityIndex(entityName);
        this.refreshTimeNotUse(index);
        return attributeModify[index];
    }

    /**
     * Questo metodo permette sapere se l'Entity in esame ha una cancellazione
     * logica o fisica
     *
     * @param entityName
     *            Nome dell'entità di cui si interessa sapere le proprietà
     * @return boolean
     * @throws Exception Impossibile trovare entity
     */
    protected synchronized boolean isLogicaldelete(String entityName)
            throws Exception {
        int index;
        index = getEntityIndex(entityName);
        this.refreshTimeNotUse(index);
        return this.convertStringYesToBoolean(logicalDelete[index]);
    }

    /**
     * Questo metodo permette sapere che gruppo di utenti ha il permesso di
     * creare questo tipo di entity
     *
     * @param entityName
     *            Nome dell'entità di cui si interessa sapere le proprietà
     * @return String
     * @throws Exception Impossibile trovare entity
     */
    protected synchronized String howCreate(String entityName) throws Exception {
        int index;
        index = getEntityIndex(entityName);
        this.refreshTimeNotUse(index);
        return creation[index];
    }

    /**
     * Questo metodo permette di sapere quali attributi sono unici e quali no
     *
     * @param entityName
     *            Nome dell'entità di cui si interessa sapere le proprietà
     * @return Array di boolean
     * @throws Exception Impossibile trovare entity
     */
    protected synchronized boolean[] isUnique(String entityName)
            throws Exception {
        int index;
        index = getEntityIndex(entityName);
        this.refreshTimeNotUse(index);
        return this.convertStringYesToBoolean(unique[index]);
    }

    /**
     * Questo metodo permette di sapere quali attributi sono inseribili e quali
     * no
     *
     * @param entityName
     *            Nome dell'entità di cui si interessa sapere le proprietà
     * @return Array di boolean
     * @throws Exception Impossibile trovare entity
     */
    protected synchronized String[] isInserible(String entityName)
            throws Exception {
        int index;
        index = getEntityIndex(entityName);
        this.refreshTimeNotUse(index);
        return inserible[index];
    }

    /**
     * Ritorna sotto forma di strigna la caption di un singolo attributo dell'entity chiamandola per nome
     * @param NameEntity nome dell'entity
     * @param NameAttribute nome dell'attributo
     * @return Caption
     * @throws Exception Impossibile trovare Attributo, Impossibile Trovare Entity
     */
    protected String getSingleAttributeCaption(String NameEntity, String NameAttribute) throws Exception {
        int index, index2 = 0;
        index = getEntityIndex(NameEntity);
        for (int i = 0; i < attributeCaption[index].length; i++) {
            if (attributeName[index][i].equals(NameAttribute)) {
                index2 = i;
            }
        }
        return attributeCaption[index][index2];
    }

        /**
     * Ritorna sotto forma di strigna la descrizione di un singolo attributo dell'entity chiamandola per nome
     * @param NameEntity nome dell'entity
     * @param NameAttribute nome dell'attributo
     * @return Descrizione
     * @throws Exception Impossibile trovare Attributo, Impossibile Trovare Entity
     */
    protected String getSingleAttributeDescriptionm(String NameEntity, String NameAttribute) throws Exception {
        int index, index2 = 0;
        index = getEntityIndex(NameEntity);
        for (int i = 0; i < attributeCaption[index].length; i++) {
            if (attributeName[index][i].equals(NameAttribute)) {
                index2 = i;
            }
        }
        return attributeDescription[index][index2];
    }

        /**
     * Ritorna sotto forma di strigna una caption di sistema chiamandola per nome
     * @param param nome della caption di sistema
     * @throws Exception Impossibile trovare la caption
     */
    protected String getSystemControlCaption(String param) throws Exception {
        String value;
        if (langCaption == null) {
            langCaption = new Properties();
            try {

                FileInputStream langFile;
                langFile = new FileInputStream(root + "conf/lang/" + language + ".conf");
                langCaption.load(langFile);
                langFile.close();

            } catch (FileNotFoundException ex) {
                throw new Exception("Error-Storage-ConfigLoader-getSystemControlCaption:LangFileNotFound" + ex.getMessage());
            }
        }
        value = langCaption.getProperty(param);
        return value;
    }

    /**
     * Ritorna il percorso radice dell'applicazione sotto forma di stringa
     * @return root
     */
    protected String getRoot() {
        return root;
    }
    /************************************************************************/
}
